// JavaScript AES implementation using ArrayBuffer
// Copyright (c) 2013 <mattias.wadman@gmail.com>
//
// MIT License:
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
//
const UA32=Uint32Array,UA8=Uint8Array
let AES = (function() {
  // spec from http://csrc.nist.gov/publications/fips/fips197/fips-197.pdf

  // for performance cipher and inv_cipher do not create new objects
  // instead they take a input buffer and pass it around to keep state
  // and it is also used as output.

  var block_size_bytes = 16;
  var block_size_words = 4;
  var nb = 4;
  var sbox = [
    0x63, 0x7c, 0x77, 0x7b, 0xf2, 0x6b, 0x6f, 0xc5, 0x30, 0x01, 0x67, 0x2b, 0xfe, 0xd7, 0xab, 0x76,
    0xca, 0x82, 0xc9, 0x7d, 0xfa, 0x59, 0x47, 0xf0, 0xad, 0xd4, 0xa2, 0xaf, 0x9c, 0xa4, 0x72, 0xc0,
    0xb7, 0xfd, 0x93, 0x26, 0x36, 0x3f, 0xf7, 0xcc, 0x34, 0xa5, 0xe5, 0xf1, 0x71, 0xd8, 0x31, 0x15,
    0x04, 0xc7, 0x23, 0xc3, 0x18, 0x96, 0x05, 0x9a, 0x07, 0x12, 0x80, 0xe2, 0xeb, 0x27, 0xb2, 0x75,
    0x09, 0x83, 0x2c, 0x1a, 0x1b, 0x6e, 0x5a, 0xa0, 0x52, 0x3b, 0xd6, 0xb3, 0x29, 0xe3, 0x2f, 0x84,
    0x53, 0xd1, 0x00, 0xed, 0x20, 0xfc, 0xb1, 0x5b, 0x6a, 0xcb, 0xbe, 0x39, 0x4a, 0x4c, 0x58, 0xcf,
    0xd0, 0xef, 0xaa, 0xfb, 0x43, 0x4d, 0x33, 0x85, 0x45, 0xf9, 0x02, 0x7f, 0x50, 0x3c, 0x9f, 0xa8,
    0x51, 0xa3, 0x40, 0x8f, 0x92, 0x9d, 0x38, 0xf5, 0xbc, 0xb6, 0xda, 0x21, 0x10, 0xff, 0xf3, 0xd2,
    0xcd, 0x0c, 0x13, 0xec, 0x5f, 0x97, 0x44, 0x17, 0xc4, 0xa7, 0x7e, 0x3d, 0x64, 0x5d, 0x19, 0x73,
    0x60, 0x81, 0x4f, 0xdc, 0x22, 0x2a, 0x90, 0x88, 0x46, 0xee, 0xb8, 0x14, 0xde, 0x5e, 0x0b, 0xdb,
    0xe0, 0x32, 0x3a, 0x0a, 0x49, 0x06, 0x24, 0x5c, 0xc2, 0xd3, 0xac, 0x62, 0x91, 0x95, 0xe4, 0x79,
    0xe7, 0xc8, 0x37, 0x6d, 0x8d, 0xd5, 0x4e, 0xa9, 0x6c, 0x56, 0xf4, 0xea, 0x65, 0x7a, 0xae, 0x08,
    0xba, 0x78, 0x25, 0x2e, 0x1c, 0xa6, 0xb4, 0xc6, 0xe8, 0xdd, 0x74, 0x1f, 0x4b, 0xbd, 0x8b, 0x8a,
    0x70, 0x3e, 0xb5, 0x66, 0x48, 0x03, 0xf6, 0x0e, 0x61, 0x35, 0x57, 0xb9, 0x86, 0xc1, 0x1d, 0x9e,
    0xe1, 0xf8, 0x98, 0x11, 0x69, 0xd9, 0x8e, 0x94, 0x9b, 0x1e, 0x87, 0xe9, 0xce, 0x55, 0x28, 0xdf,
    0x8c, 0xa1, 0x89, 0x0d, 0xbf, 0xe6, 0x42, 0x68, 0x41, 0x99, 0x2d, 0x0f, 0xb0, 0x54, 0xbb, 0x16
  ];

  var inv_sbox = [
    0x52, 0x09, 0x6a, 0xd5, 0x30, 0x36, 0xa5, 0x38, 0xbf, 0x40, 0xa3, 0x9e, 0x81, 0xf3, 0xd7, 0xfb,
    0x7c, 0xe3, 0x39, 0x82, 0x9b, 0x2f, 0xff, 0x87, 0x34, 0x8e, 0x43, 0x44, 0xc4, 0xde, 0xe9, 0xcb,
    0x54, 0x7b, 0x94, 0x32, 0xa6, 0xc2, 0x23, 0x3d, 0xee, 0x4c, 0x95, 0x0b, 0x42, 0xfa, 0xc3, 0x4e,
    0x08, 0x2e, 0xa1, 0x66, 0x28, 0xd9, 0x24, 0xb2, 0x76, 0x5b, 0xa2, 0x49, 0x6d, 0x8b, 0xd1, 0x25,
    0x72, 0xf8, 0xf6, 0x64, 0x86, 0x68, 0x98, 0x16, 0xd4, 0xa4, 0x5c, 0xcc, 0x5d, 0x65, 0xb6, 0x92,
    0x6c, 0x70, 0x48, 0x50, 0xfd, 0xed, 0xb9, 0xda, 0x5e, 0x15, 0x46, 0x57, 0xa7, 0x8d, 0x9d, 0x84,
    0x90, 0xd8, 0xab, 0x00, 0x8c, 0xbc, 0xd3, 0x0a, 0xf7, 0xe4, 0x58, 0x05, 0xb8, 0xb3, 0x45, 0x06,
    0xd0, 0x2c, 0x1e, 0x8f, 0xca, 0x3f, 0x0f, 0x02, 0xc1, 0xaf, 0xbd, 0x03, 0x01, 0x13, 0x8a, 0x6b,
    0x3a, 0x91, 0x11, 0x41, 0x4f, 0x67, 0xdc, 0xea, 0x97, 0xf2, 0xcf, 0xce, 0xf0, 0xb4, 0xe6, 0x73,
    0x96, 0xac, 0x74, 0x22, 0xe7, 0xad, 0x35, 0x85, 0xe2, 0xf9, 0x37, 0xe8, 0x1c, 0x75, 0xdf, 0x6e,
    0x47, 0xf1, 0x1a, 0x71, 0x1d, 0x29, 0xc5, 0x89, 0x6f, 0xb7, 0x62, 0x0e, 0xaa, 0x18, 0xbe, 0x1b,
    0xfc, 0x56, 0x3e, 0x4b, 0xc6, 0xd2, 0x79, 0x20, 0x9a, 0xdb, 0xc0, 0xfe, 0x78, 0xcd, 0x5a, 0xf4,
    0x1f, 0xdd, 0xa8, 0x33, 0x88, 0x07, 0xc7, 0x31, 0xb1, 0x12, 0x10, 0x59, 0x27, 0x80, 0xec, 0x5f,
    0x60, 0x51, 0x7f, 0xa9, 0x19, 0xb5, 0x4a, 0x0d, 0x2d, 0xe5, 0x7a, 0x9f, 0x93, 0xc9, 0x9c, 0xef,
    0xa0, 0xe0, 0x3b, 0x4d, 0xae, 0x2a, 0xf5, 0xb0, 0xc8, 0xeb, 0xbb, 0x3c, 0x83, 0x53, 0x99, 0x61,
    0x17, 0x2b, 0x04, 0x7e, 0xba, 0x77, 0xd6, 0x26, 0xe1, 0x69, 0x14, 0x63, 0x55, 0x21, 0x0c, 0x7d
  ];

  var rcon = [
    0x00000000,
    0x01000000,
    0x02000000,
    0x04000000,
    0x08000000,
    0x10000000,
    0x20000000,
    0x40000000,
    0x80000000,
    0x1b000000,
    0x36000000
  ];

  // from http://www.cs.utsa.edu/~wagner/laws/FFM.html
  function ffmul(a, b) {
    var r = 0;
    var t;

    while (a) {
      if ((a & 1)) {
        r = r ^ b;
      }

      t = b & 0x80;
      b <<= 1;
      if (t) {
        b ^= 0x1b;
      }

      a >>= 1;
    }

    return r;
  }

  function ffmul_table(n) {
    var table = new Array(256);

    for (var i = 0; i < 256; i++) {
      table[i] = ffmul(n, i) & 0xff;
    }

    return table;
  }

  // precompute for factors used in (inv)mix_columns
  var ffmul_t_2 = ffmul_table(2);
  var ffmul_t_3 = ffmul_table(3);
  var ffmul_t_9 = ffmul_table(9);
  var ffmul_t_b = ffmul_table(0xb);
  var ffmul_t_d = ffmul_table(0xd);
  var ffmul_t_e = ffmul_table(0xe);

  function sub_word(w) {
    return (
        sbox[w >>> 24] << 24 |
        sbox[(w >>> 16) & 0xff] << 16 |
        sbox[(w >>> 8) & 0xff] << 8 |
        sbox[w & 0xff]
    );
  }

  function rot_word(w) {
    return w >>> 24 | w << 8;
  }

  function endian_swap32(n) {
    return n >>> 24 | (n & 0xff0000) >>> 8 | (n & 0xff00) << 8 | (n & 0xff) << 24;
  }

  // little endian if first byte in 32 bit uint 1 is 1
  var is_little_endian = ((new UA8((new UA32([1])).buffer))[0] == 1);

  // key is 16, 24 or 32 byte ArrayBuffer
  function key_expansion(key) {
    var key_u8 = new UA8(key);
    var nk = key_u8.length / 4;
    var nr = nk + 6;

    var w = new UA32(nb * (nr+1));
    for (var i = 0; i < nk; i++) {
      w[i] = (
          key_u8[i*4] << 24 |
          key_u8[i*4+1] << 16 |
          key_u8[i*4+2] << 8 |
          key_u8[i*4+3]
      );
    }

    for (var i = nk; i < nb*(nr+1); i++) {
      var temp = w[i - 1];
      if (i % nk == 0) {
        temp = sub_word(rot_word(temp)) ^ rcon[i/nk];
      } else if (nk > 6 && i % nk == 4) {
        temp = sub_word(temp);
      }

      w[i] = w[i - nk] ^ temp;
    }

    // make sure key schedule byte order matches state byte order
    // this is so that correct bytes are xored for add_round_key
    if (is_little_endian) {
      for (var i = 0; i < w.length; i++) {
        w[i] = endian_swap32(w[i]);
      }
    }

    return w;
  }

  // state_u8 is 16 byte UA8 (also used as output)
  // w is value from key_expansion
  function cipher(state_u32, w) {
    var nr = (w.length / nb - 1)*4;
    var s0, s1, s2, s3;
    var t0, t1, t2, t3;
    var m0, m1, m2, m3;

    // add_round_key
    s0 = (state_u32[0] ^ w[0]) >>> 0;
    s1 = (state_u32[1] ^ w[1]) >>> 0;
    s2 = (state_u32[2] ^ w[2]) >>> 0;
    s3 = (state_u32[3] ^ w[3]) >>> 0;

    for (var round = 4; round < nr; round += 4) {
      // sub_byte, shift_rows, mix_columns, add_round_key

      t0 = s0;
      t1 = s1;
      t2 = s2;
      t3 = s3;

      m0 = sbox[t0 & 0xff] >>> 0;
      m1 = sbox[(t1 >>> 8) & 0xff] >>> 0;
      m2 = sbox[(t2 >>> 16) & 0xff] >>> 0;
      m3 = sbox[t3 >>> 24] >>> 0;
      s0 = ((
          (ffmul_t_2[m0] ^ ffmul_t_3[m1] ^ m2 ^ m3) |
          (m0 ^ ffmul_t_2[m1] ^ ffmul_t_3[m2] ^ m3) << 8 |
          (m0 ^ m1 ^ ffmul_t_2[m2] ^ ffmul_t_3[m3]) << 16 |
          (ffmul_t_3[m0] ^ m1 ^ m2 ^ ffmul_t_2[m3]) << 24
      ) ^ w[round]) >>> 0;
      m0 = sbox[t1 & 0xff] >>> 0;
      m1 = sbox[(t2 >>> 8) & 0xff] >>> 0;
      m2 = sbox[(t3 >>> 16) & 0xff] >>> 0;
      m3 = sbox[t0 >>> 24] >>> 0;
      s1 = ((
          (ffmul_t_2[m0] ^ ffmul_t_3[m1] ^ m2 ^ m3) |
          (m0 ^ ffmul_t_2[m1] ^ ffmul_t_3[m2] ^ m3) << 8 |
          (m0 ^ m1 ^ ffmul_t_2[m2] ^ ffmul_t_3[m3]) << 16 |
          (ffmul_t_3[m0] ^ m1 ^ m2 ^ ffmul_t_2[m3]) << 24
      ) ^ w[round+1]) >>> 0;
      m0 = sbox[t2 & 0xff] >>> 0;
      m1 = sbox[(t3 >>> 8) & 0xff] >>> 0;
      m2 = sbox[(t0 >>> 16) & 0xff] >>> 0;
      m3 = sbox[t1 >>> 24] >>> 0;
      s2 = ((
          (ffmul_t_2[m0] ^ ffmul_t_3[m1] ^ m2 ^ m3) |
          (m0 ^ ffmul_t_2[m1] ^ ffmul_t_3[m2] ^ m3) << 8 |
          (m0 ^ m1 ^ ffmul_t_2[m2] ^ ffmul_t_3[m3]) << 16 |
          (ffmul_t_3[m0] ^ m1 ^ m2 ^ ffmul_t_2[m3]) << 24
      ) ^ w[round+2]) >>> 0;
      m0 = sbox[t3 & 0xff] >>> 0;
      m1 = sbox[(t0 >>> 8) & 0xff] >>> 0;
      m2 = sbox[(t1 >>> 16) & 0xff] >>> 0;
      m3 = sbox[t2 >>> 24] >>> 0;
      s3 = ((
          (ffmul_t_2[m0] ^ ffmul_t_3[m1] ^ m2 ^ m3) |
          (m0 ^ ffmul_t_2[m1] ^ ffmul_t_3[m2] ^ m3) << 8 |
          (m0 ^ m1 ^ ffmul_t_2[m2] ^ ffmul_t_3[m3]) << 16 |
          (ffmul_t_3[m0] ^ m1 ^ m2 ^ ffmul_t_2[m3]) << 24
      ) ^ w[round+3]) >>> 0;
    }

    // sub_byte, shift_rows, add_round_key
    state_u32[0] = w[nr] ^ (sbox[s0 & 0xff] | sbox[(s1 >>> 8) & 0xff] << 8 | sbox[(s2 >>> 16) & 0xff] << 16 | sbox[s3 >>> 24] << 24);
    state_u32[1] = w[nr+1] ^ (sbox[s1 & 0xff] | sbox[(s2 >>> 8) & 0xff] << 8 | sbox[(s3 >>> 16) & 0xff] << 16 | sbox[s0 >>> 24] << 24);
    state_u32[2] = w[nr+2] ^ (sbox[s2 & 0xff] | sbox[(s3 >>> 8) & 0xff] << 8 | sbox[(s0 >>> 16) & 0xff] << 16 | sbox[s1 >>> 24] << 24);
    state_u32[3] = w[nr+3] ^ (sbox[s3 & 0xff] | sbox[(s0 >>> 8) & 0xff] << 8 | sbox[(s1 >>> 16) & 0xff] << 16 | sbox[s2 >>> 24] << 24);
  }

  // state_u8 is 16 byte UA8 (also used as output)
  // w is value from key_expansion
  function inv_cipher(state_u32, w) {
    var nr = (w.length / nb - 1) * 4;
    var s0, s1, s2, s3;
    var t0, t1, t2, t3;

    // add_round_key
    s0 = (state_u32[0] ^ w[nr]) >>> 0;
    s1 = (state_u32[1] ^ w[nr+1]) >>> 0;
    s2 = (state_u32[2] ^ w[nr+2]) >>> 0;
    s3 = (state_u32[3] ^ w[nr+3]) >>> 0;

    for (var round = nr-4; round > 0; round -= 4) {
      // inv_shift_rows, inv_sub_byte, add_round_key

      t0 = s0;
      t1 = s1;
      t2 = s2;
      t3 = s3;
      s0 = ((
          inv_sbox[t0 & 0xff] | inv_sbox[(t3 >>> 8) & 0xff] << 8 | inv_sbox[(t2 >>> 16) & 0xff] << 16 | inv_sbox[t1 >>> 24] << 24
      ) ^ w[round]) >>> 0;
      s1 = ((
          inv_sbox[t1 & 0xff] | inv_sbox[(t0 >>> 8) & 0xff] << 8 | inv_sbox[(t3 >>> 16) & 0xff] << 16 | inv_sbox[t2 >>> 24] << 24
      ) ^ w[round+1]) >>> 0;
      s2 = ((
          inv_sbox[t2 & 0xff] | inv_sbox[(t1 >>> 8) & 0xff] << 8 | inv_sbox[(t0 >>> 16) & 0xff] << 16 | inv_sbox[t3 >>> 24] << 24
      ) ^ w[round+2]) >>> 0;
      s3 = ((
          inv_sbox[t3 & 0xff] | inv_sbox[(t2 >>> 8) & 0xff] << 8 | inv_sbox[(t1 >>> 16) & 0xff] << 16 | inv_sbox[t0 >>> 24] << 24
      ) ^ w[round+3]) >>> 0;

      // inv_mix_columns
      t0 = s0 & 0xff >>> 0;
      t1 = ((s0 >>> 8) & 0xff) >>>0;
      t2 = ((s0 >>> 16) & 0xff) >>> 0;
      t3 = s0 >>> 24;
      s0 = (
          (ffmul_t_e[t0] ^ ffmul_t_b[t1] ^ ffmul_t_d[t2] ^ ffmul_t_9[t3]) |
          (ffmul_t_9[t0] ^ ffmul_t_e[t1] ^ ffmul_t_b[t2] ^ ffmul_t_d[t3]) << 8 |
          (ffmul_t_d[t0] ^ ffmul_t_9[t1] ^ ffmul_t_e[t2] ^ ffmul_t_b[t3]) << 16 |
          (ffmul_t_b[t0] ^ ffmul_t_d[t1] ^ ffmul_t_9[t2] ^ ffmul_t_e[t3]) << 24
      ) >>> 0;
      t0 = (s1 & 0xff) >>> 0;
      t1 = ((s1 >>> 8) & 0xff) >>> 0;
      t2 = ((s1 >>> 16) & 0xff) >>> 0;
      t3 = s1 >>> 24;
      s1 = (
          (ffmul_t_e[t0] ^ ffmul_t_b[t1] ^ ffmul_t_d[t2] ^ ffmul_t_9[t3]) |
          (ffmul_t_9[t0] ^ ffmul_t_e[t1] ^ ffmul_t_b[t2] ^ ffmul_t_d[t3]) << 8 |
          (ffmul_t_d[t0] ^ ffmul_t_9[t1] ^ ffmul_t_e[t2] ^ ffmul_t_b[t3]) << 16 |
          (ffmul_t_b[t0] ^ ffmul_t_d[t1] ^ ffmul_t_9[t2] ^ ffmul_t_e[t3]) << 24
      ) >>> 0;
      t0 = (s2 & 0xff) >>> 0;
      t1 = ((s2 >>> 8) & 0xff) >>> 0;
      t2 = ((s2 >>> 16) & 0xff) >>> 0;
      t3 = s2 >>> 24;
      s2 = (
          (ffmul_t_e[t0] ^ ffmul_t_b[t1] ^ ffmul_t_d[t2] ^ ffmul_t_9[t3]) |
          (ffmul_t_9[t0] ^ ffmul_t_e[t1] ^ ffmul_t_b[t2] ^ ffmul_t_d[t3]) << 8 |
          (ffmul_t_d[t0] ^ ffmul_t_9[t1] ^ ffmul_t_e[t2] ^ ffmul_t_b[t3]) << 16 |
          (ffmul_t_b[t0] ^ ffmul_t_d[t1] ^ ffmul_t_9[t2] ^ ffmul_t_e[t3]) << 24
      ) >>> 0;
      t0 = (s3 & 0xff) >>> 0;
      t1 = ((s3 >>> 8) & 0xff) >>> 0;
      t2 = ((s3 >>> 16) & 0xff) >>> 0;
      t3 = s3 >>> 24;
      s3 = (
          (ffmul_t_e[t0] ^ ffmul_t_b[t1] ^ ffmul_t_d[t2] ^ ffmul_t_9[t3]) |
          (ffmul_t_9[t0] ^ ffmul_t_e[t1] ^ ffmul_t_b[t2] ^ ffmul_t_d[t3]) << 8 |
          (ffmul_t_d[t0] ^ ffmul_t_9[t1] ^ ffmul_t_e[t2] ^ ffmul_t_b[t3]) << 16 |
          (ffmul_t_b[t0] ^ ffmul_t_d[t1] ^ ffmul_t_9[t2] ^ ffmul_t_e[t3]) << 24
      ) >>> 0;
    }

    // inv_shift_rows, inv_sub_byte, add_round_key
    state_u32[0] = w[0] ^ (inv_sbox[s0 & 0xff] | inv_sbox[(s3 >>> 8) & 0xff] << 8 | inv_sbox[(s2 >>> 16) & 0xff] << 16 | inv_sbox[s1 >>> 24] << 24);
    state_u32[1] = w[1] ^ (inv_sbox[s1 & 0xff] | inv_sbox[(s0 >>> 8) & 0xff] << 8 | inv_sbox[(s3 >>> 16) & 0xff] << 16 | inv_sbox[s2 >>> 24] << 24);
    state_u32[2] = w[2] ^ (inv_sbox[s2 & 0xff] | inv_sbox[(s1 >>> 8) & 0xff] << 8 | inv_sbox[(s0 >>> 16) & 0xff] << 16 | inv_sbox[s3 >>> 24] << 24);
    state_u32[3] = w[3] ^ (inv_sbox[s3 & 0xff] | inv_sbox[(s2 >>> 8) & 0xff] << 8 | inv_sbox[(s1 >>> 16) & 0xff] << 16 | inv_sbox[s0 >>> 24] << 24);
  }

  return {
    block_size_bytes: block_size_bytes,
    block_size_words: block_size_words,
    endian_swap32: endian_swap32,
    is_little_endian: is_little_endian,
    key_expansion: key_expansion,
    cipher: cipher,
    inv_cipher: inv_cipher
  };
})();
// JavaScript CBC and PKCS padding implementation using ArrayBuffer
// Copyright (c) 2013 <mattias.wadman@gmail.com>
//
// MIT License:
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
//

let Crypto = (function() {
  // CBC spec from http://csrc.nist.gov/publications/nistpubs/800-38a/sp800-38a.pdf
  // PKCS padding spec from http://tools.ietf.org/html/rfc2315

  var pkcs_max_pad_byte_count = 16;

  // both encrypt_aes_cbc and decrypt_aes_cbc are written to not
  // creating new objects in the block loop.
  // some implementation notes:
  // the manual "copy" in the loop start is due to that .subarray
  // creates new view objects.

  // input is ArrayBuffer with 16 byte aligned length
  // key is 16, 24 or 32 byte ArrayBuffer
  // iv is 16 byte ArrayBuffer
  function encrypt_aes_cbc(input, key, iv) {
    var input_u32 = new UA32(input);
    var output_u32 = new UA32(input_u32.length);
    var state_block_u32 = new UA32(AES.block_size_words);
    var prev_cipher_block_u32 = new UA32(AES.block_size_words);
    var w = AES.key_expansion(key);

    prev_cipher_block_u32.set(new UA32(iv));

    for (var i = 0; i < input_u32.length; i += AES.block_size_words) {
      state_block_u32[0] = input_u32[i+0];
      state_block_u32[1] = input_u32[i+1];
      state_block_u32[2] = input_u32[i+2];
      state_block_u32[3] = input_u32[i+3];

      for (var j = 0; j < AES.block_size_words; j++) {
        state_block_u32[j] ^= prev_cipher_block_u32[j];
      }

      AES.cipher(state_block_u32, w);
      output_u32.set(state_block_u32, i);

      prev_cipher_block_u32.set(state_block_u32);
    }

    return output_u32.buffer;
  }

  // input is ArrayBuffer with 16 byte aligned length
  // key is 16, 24 or 32 byte ArrayBuffer
  // iv is 16 byte ArrayBuffer
  function decrypt_aes_cbc(input, key, iv) {
    var input_u32 = new UA32(input);
    var output_u32 = new UA32(input_u32.length);
    var state_block_u32 = new UA32(AES.block_size_words);
    var input_block_u32 = new UA32(AES.block_size_words);
    var prev_input_block_u32 = new UA32(AES.block_size_words);
    var w = AES.key_expansion(key);

    prev_input_block_u32.set(new UA32(iv));

    for (var i = 0; i < input_u32.length; i += AES.block_size_words) {
      input_block_u32[0] = input_u32[i+0];
      input_block_u32[1] = input_u32[i+1];
      input_block_u32[2] = input_u32[i+2];
      input_block_u32[3] = input_u32[i+3];

      state_block_u32.set(input_block_u32);
      AES.inv_cipher(state_block_u32, w);

      for (var j = 0; j < AES.block_size_words; j++) {
        state_block_u32[j] ^= prev_input_block_u32[j];
      }
      output_u32.set(state_block_u32, i);

      prev_input_block_u32.set(input_block_u32);
    }

    return output_u32.buffer;
  }

  // returns a new buffer with only the padding bytes
  function pkcs_pad_buffer_to_append(length) {
    var pad_byte_count = pkcs_max_pad_byte_count - (length % pkcs_max_pad_byte_count);
    var pad_u8 = new UA8(pad_byte_count);
    for (var i = 0; i < pad_byte_count; i++) {
      pad_u8[i] = pad_byte_count;
    }

    return pad_u8.buffer;
  }

  // returns a new buffer with added padding
  function pkcs_pad(buffer) {
    var pad_buffer = pkcs_pad_buffer_to_append(buffer.byteLength);
    var padded_u8 = new UA8(buffer.byteLength + pad_buffer.byteLength);
    padded_u8.set(new UA8(buffer), 0);
    padded_u8.set(new UA8(pad_buffer), buffer.byteLength);

    return padded_u8.buffer;
  }

  // return number of bytes at end of buffer that is padding
  // returns 0 if buffer has invalid pkcs padding
  function pkcs_pad_byte_count(buffer) {
    var padded_u8 = new UA8(buffer);
    if (padded_u8.length == 0) {
      return 0;
    }

    var count = padded_u8[padded_u8.length-1];
    if (count > padded_u8.length || count > pkcs_max_pad_byte_count) {
      return 0;
    }

    return count;
  }

  // returns a new buffer with padding removed
  function pkcs_unpad(buffer) {
    var unpadded_length = buffer.byteLength - pkcs_pad_byte_count(buffer);

    // HACK: slice missing in older versions of nodejs
    if (!buffer.slice) {
      var padded_bytes = new UA8(buffer);
      var unpadded = new UA8(unpadded_length);
      unpadded.set(padded_bytes.subarray(0, unpadded_length));
      return unpadded.buffer;
    }

    return buffer.slice(0, unpadded_length);
  }

  return {
    encrypt: encrypt_aes_cbc,
    decrypt: decrypt_aes_cbc,
    pkcs_pad_buffer_to_append: pkcs_pad_buffer_to_append,
    pkcs_pad: pkcs_pad,
    pkcs_pad_byte_count: pkcs_pad_byte_count,
    pkcs_unpad: pkcs_unpad
  };
})();

var ConvertHex = (function() {
  function toBytes(text) {
    var result = [];
    for (var i = 0; i < text.length; i += 2) {
      result.push(parseInt(text.substr(i, 2), 16));
    }

    return result;
  }

  // http://ixti.net/development/javascript/2011/11/11/base64-encodedecode-of-utf8-in-browser-with-js.html
  var Hex = '0123456789abcdef';

  function fromBytes(bytes) {
    var result = [];
    for (var i = 0; i < bytes.length; i++) {
      var v = bytes[i];
      result.push(Hex[(v & 0xf0) >> 4] + Hex[v & 0x0f]);
    }
    return result.join('');
  }

  return {
    toBytes: toBytes,
    fromBytes: fromBytes,
  }
})();
function checkInt(value) {
  return (parseInt(value) === value);
}

function checkInts(arrayish) {
  if (!checkInt(arrayish.length)) { return false; }

  for (var i = 0; i < arrayish.length; i++) {
    if (!checkInt(arrayish[i]) || arrayish[i] < 0 || arrayish[i] > 255) {
      return false;
    }
  }

  return true;
}

function coerceArray(arg, copy) {

  // ArrayBuffer view
  if (arg.buffer && arg.name === 'Uint8Array') {

    if (copy) {
      if (arg.slice) {
        arg = arg.slice();
      } else {
        arg = Array.prototype.slice.call(arg);
      }
    }

    return arg;
  }

  // It's an array; check it is a valid representation of a byte
  if (Array.isArray(arg)) {
    if (!checkInts(arg)) {
      throw new Error('Array contains invalid value: ' + arg);
    }

    return new UA8(arg);
  }

  // Something else, but behaves like an array (maybe a Buffer? Arguments?)
  if (checkInt(arg.length) && checkInts(arg)) {
    return new UA8(arg);
  }

  throw new Error('unsupported array-like object');
}
var ConvertUtf8 = (function() {
  function toBytes(text) {
    var result = [], i = 0;
    text = encodeURI(text);
    while (i < text.length) {
      var c = text.charCodeAt(i++);

      // if it is a % sign, encode the following 2 bytes as a hex value
      if (c === 37) {
        result.push(parseInt(text.substr(i, 2), 16))
        i += 2;

        // otherwise, just the actual byte
      } else {
        result.push(c)
      }
    }

    return coerceArray(result);
  }

  function fromBytes(bytes) {
    var result = [], i = 0;

    while (i < bytes.length) {
      var c = bytes[i];

      if (c < 128) {
        result.push(String.fromCharCode(c));
        i++;
      } else if (c > 191 && c < 224) {
        result.push(String.fromCharCode(((c & 0x1f) << 6) | (bytes[i + 1] & 0x3f)));
        i += 2;
      } else {
        result.push(String.fromCharCode(((c & 0x0f) << 12) | ((bytes[i + 1] & 0x3f) << 6) | (bytes[i + 2] & 0x3f)));
        i += 3;
      }
    }

    return result.join('');
  }

  return {
    toBytes: toBytes,
    fromBytes: fromBytes,
  }
})();

/**
 * Add integers, wrapping at 2^32.
 * This uses 16-bit operations internally to work around bugs in interpreters.
 *
 * @param {number} x First integer
 * @param {number} y Second integer
 * @returns {number} Sum
 */
function safeAdd(x, y) {
  var lsw = (x & 0xffff) + (y & 0xffff)
  var msw = (x >> 16) + (y >> 16) + (lsw >> 16)
  return (msw << 16) | (lsw & 0xffff)
}

/**
 * Bitwise rotate a 32-bit number to the left.
 *
 * @param {number} num 32-bit number
 * @param {number} cnt Rotation count
 * @returns {number} Rotated number
 */
function bitRotateLeft(num, cnt) {
  return (num << cnt) | (num >>> (32 - cnt))
}

/**
 * Basic operation the algorithm uses.
 *
 * @param {number} q q
 * @param {number} a a
 * @param {number} b b
 * @param {number} x x
 * @param {number} s s
 * @param {number} t t
 * @returns {number} Result
 */
function md5cmn(q, a, b, x, s, t) {
  return safeAdd(bitRotateLeft(safeAdd(safeAdd(a, q), safeAdd(x, t)), s), b)
}

/**
 * Basic operation the algorithm uses.
 *
 * @param {number} a a
 * @param {number} b b
 * @param {number} c c
 * @param {number} d d
 * @param {number} x x
 * @param {number} s s
 * @param {number} t t
 * @returns {number} Result
 */
function md5ff(a, b, c, d, x, s, t) {
  return md5cmn((b & c) | (~b & d), a, b, x, s, t)
}

/**
 * Basic operation the algorithm uses.
 *
 * @param {number} a a
 * @param {number} b b
 * @param {number} c c
 * @param {number} d d
 * @param {number} x x
 * @param {number} s s
 * @param {number} t t
 * @returns {number} Result
 */
function md5gg(a, b, c, d, x, s, t) {
  return md5cmn((b & d) | (c & ~d), a, b, x, s, t)
}

/**
 * Basic operation the algorithm uses.
 *
 * @param {number} a a
 * @param {number} b b
 * @param {number} c c
 * @param {number} d d
 * @param {number} x x
 * @param {number} s s
 * @param {number} t t
 * @returns {number} Result
 */
function md5hh(a, b, c, d, x, s, t) {
  return md5cmn(b ^ c ^ d, a, b, x, s, t)
}

/**
 * Basic operation the algorithm uses.
 *
 * @param {number} a a
 * @param {number} b b
 * @param {number} c c
 * @param {number} d d
 * @param {number} x x
 * @param {number} s s
 * @param {number} t t
 * @returns {number} Result
 */
function md5ii(a, b, c, d, x, s, t) {
  return md5cmn(c ^ (b | ~d), a, b, x, s, t)
}

/**
 * Calculate the MD5 of an array of little-endian words, and a bit length.
 *
 * @param {Array} x Array of little-endian words
 * @param {number} len Bit length
 * @returns {Array<number>} MD5 Array
 */
function binlMD5(x, len) {
  /* append padding */
  x[len >> 5] |= 0x80 << len % 32
  x[(((len + 64) >>> 9) << 4) + 14] = len

  var i
  var olda
  var oldb
  var oldc
  var oldd
  var a = 1732584193
  var b = -271733879
  var c = -1732584194
  var d = 271733878

  for (i = 0; i < x.length; i += 16) {
    olda = a
    oldb = b
    oldc = c
    oldd = d

    a = md5ff(a, b, c, d, x[i], 7, -680876936)
    d = md5ff(d, a, b, c, x[i + 1], 12, -389564586)
    c = md5ff(c, d, a, b, x[i + 2], 17, 606105819)
    b = md5ff(b, c, d, a, x[i + 3], 22, -1044525330)
    a = md5ff(a, b, c, d, x[i + 4], 7, -176418897)
    d = md5ff(d, a, b, c, x[i + 5], 12, 1200080426)
    c = md5ff(c, d, a, b, x[i + 6], 17, -1473231341)
    b = md5ff(b, c, d, a, x[i + 7], 22, -45705983)
    a = md5ff(a, b, c, d, x[i + 8], 7, 1770035416)
    d = md5ff(d, a, b, c, x[i + 9], 12, -1958414417)
    c = md5ff(c, d, a, b, x[i + 10], 17, -42063)
    b = md5ff(b, c, d, a, x[i + 11], 22, -1990404162)
    a = md5ff(a, b, c, d, x[i + 12], 7, 1804603682)
    d = md5ff(d, a, b, c, x[i + 13], 12, -40341101)
    c = md5ff(c, d, a, b, x[i + 14], 17, -1502002290)
    b = md5ff(b, c, d, a, x[i + 15], 22, 1236535329)

    a = md5gg(a, b, c, d, x[i + 1], 5, -165796510)
    d = md5gg(d, a, b, c, x[i + 6], 9, -1069501632)
    c = md5gg(c, d, a, b, x[i + 11], 14, 643717713)
    b = md5gg(b, c, d, a, x[i], 20, -373897302)
    a = md5gg(a, b, c, d, x[i + 5], 5, -701558691)
    d = md5gg(d, a, b, c, x[i + 10], 9, 38016083)
    c = md5gg(c, d, a, b, x[i + 15], 14, -660478335)
    b = md5gg(b, c, d, a, x[i + 4], 20, -405537848)
    a = md5gg(a, b, c, d, x[i + 9], 5, 568446438)
    d = md5gg(d, a, b, c, x[i + 14], 9, -1019803690)
    c = md5gg(c, d, a, b, x[i + 3], 14, -187363961)
    b = md5gg(b, c, d, a, x[i + 8], 20, 1163531501)
    a = md5gg(a, b, c, d, x[i + 13], 5, -1444681467)
    d = md5gg(d, a, b, c, x[i + 2], 9, -51403784)
    c = md5gg(c, d, a, b, x[i + 7], 14, 1735328473)
    b = md5gg(b, c, d, a, x[i + 12], 20, -1926607734)

    a = md5hh(a, b, c, d, x[i + 5], 4, -378558)
    d = md5hh(d, a, b, c, x[i + 8], 11, -2022574463)
    c = md5hh(c, d, a, b, x[i + 11], 16, 1839030562)
    b = md5hh(b, c, d, a, x[i + 14], 23, -35309556)
    a = md5hh(a, b, c, d, x[i + 1], 4, -1530992060)
    d = md5hh(d, a, b, c, x[i + 4], 11, 1272893353)
    c = md5hh(c, d, a, b, x[i + 7], 16, -155497632)
    b = md5hh(b, c, d, a, x[i + 10], 23, -1094730640)
    a = md5hh(a, b, c, d, x[i + 13], 4, 681279174)
    d = md5hh(d, a, b, c, x[i], 11, -358537222)
    c = md5hh(c, d, a, b, x[i + 3], 16, -722521979)
    b = md5hh(b, c, d, a, x[i + 6], 23, 76029189)
    a = md5hh(a, b, c, d, x[i + 9], 4, -640364487)
    d = md5hh(d, a, b, c, x[i + 12], 11, -421815835)
    c = md5hh(c, d, a, b, x[i + 15], 16, 530742520)
    b = md5hh(b, c, d, a, x[i + 2], 23, -995338651)

    a = md5ii(a, b, c, d, x[i], 6, -198630844)
    d = md5ii(d, a, b, c, x[i + 7], 10, 1126891415)
    c = md5ii(c, d, a, b, x[i + 14], 15, -1416354905)
    b = md5ii(b, c, d, a, x[i + 5], 21, -57434055)
    a = md5ii(a, b, c, d, x[i + 12], 6, 1700485571)
    d = md5ii(d, a, b, c, x[i + 3], 10, -1894986606)
    c = md5ii(c, d, a, b, x[i + 10], 15, -1051523)
    b = md5ii(b, c, d, a, x[i + 1], 21, -2054922799)
    a = md5ii(a, b, c, d, x[i + 8], 6, 1873313359)
    d = md5ii(d, a, b, c, x[i + 15], 10, -30611744)
    c = md5ii(c, d, a, b, x[i + 6], 15, -1560198380)
    b = md5ii(b, c, d, a, x[i + 13], 21, 1309151649)
    a = md5ii(a, b, c, d, x[i + 4], 6, -145523070)
    d = md5ii(d, a, b, c, x[i + 11], 10, -1120210379)
    c = md5ii(c, d, a, b, x[i + 2], 15, 718787259)
    b = md5ii(b, c, d, a, x[i + 9], 21, -343485551)

    a = safeAdd(a, olda)
    b = safeAdd(b, oldb)
    c = safeAdd(c, oldc)
    d = safeAdd(d, oldd)
  }
  return [a, b, c, d]
}

/**
 * Convert an array of little-endian words to a string
 *
 * @param {Array<number>} input MD5 Array
 * @returns {string} MD5 string
 */
function binl2rstr(input) {
  var i
  var output = ''
  var length32 = input.length * 32
  for (i = 0; i < length32; i += 8) {
    output += String.fromCharCode((input[i >> 5] >>> i % 32) & 0xff)
  }
  return output
}

/**
 * Convert a raw string to an array of little-endian words
 * Characters >255 have their high-byte silently ignored.
 *
 * @param {string} input Raw input string
 * @returns {Array<number>} Array of little-endian words
 */
function rstr2binl(input) {
  var i
  var output = []
  output[(input.length >> 2) - 1] = undefined
  for (i = 0; i < output.length; i += 1) {
    output[i] = 0
  }
  var length8 = input.length * 8
  for (i = 0; i < length8; i += 8) {
    output[i >> 5] |= (input.charCodeAt(i / 8) & 0xff) << i % 32
  }
  return output
}

/**
 * Calculate the MD5 of a raw string
 *
 * @param {string} s Input string
 * @returns {string} Raw MD5 string
 */
function rstrMD5(s) {
  return binl2rstr(binlMD5(rstr2binl(s), s.length * 8))
}

/**
 * Calculates the HMAC-MD5 of a key and some data (raw strings)
 *
 * @param {string} key HMAC key
 * @param {string} data Raw input string
 * @returns {string} Raw MD5 string
 */
function rstrHMACMD5(key, data) {
  var i
  var bkey = rstr2binl(key)
  var ipad = []
  var opad = []
  var hash
  ipad[15] = opad[15] = undefined
  if (bkey.length > 16) {
    bkey = binlMD5(bkey, key.length * 8)
  }
  for (i = 0; i < 16; i += 1) {
    ipad[i] = bkey[i] ^ 0x36363636
    opad[i] = bkey[i] ^ 0x5c5c5c5c
  }
  hash = binlMD5(ipad.concat(rstr2binl(data)), 512 + data.length * 8)
  return binl2rstr(binlMD5(opad.concat(hash), 512 + 128))
}

/**
 * Convert a raw string to a hex string
 *
 * @param {string} input Raw input string
 * @returns {string} Hex encoded string
 */
function rstr2hex(input) {
  var hexTab = '0123456789abcdef'
  var output = ''
  var x
  var i
  for (i = 0; i < input.length; i += 1) {
    x = input.charCodeAt(i)
    output += hexTab.charAt((x >>> 4) & 0x0f) + hexTab.charAt(x & 0x0f)
  }
  return output
}

/**
 * Encode a string as UTF-8
 *
 * @param {string} input Input string
 * @returns {string} UTF8 string
 */
function str2rstrUTF8(input) {
  return unescape(encodeURIComponent(input))
}

/**
 * Encodes input string as raw MD5 string
 *
 * @param {string} s Input string
 * @returns {string} Raw MD5 string
 */
function rawMD5(s) {
  return rstrMD5(str2rstrUTF8(s))
}

/**
 * Encodes input string as Hex encoded string
 *
 * @param {string} s Input string
 * @returns {string} Hex encoded string
 */
function hexMD5(s) {
  return rstr2hex(rawMD5(s))
}

/**
 * Calculates the raw HMAC-MD5 for the given key and data
 *
 * @param {string} k HMAC key
 * @param {string} d Input string
 * @returns {string} Raw MD5 string
 */
function rawHMACMD5(k, d) {
  return rstrHMACMD5(str2rstrUTF8(k), str2rstrUTF8(d))
}

/**
 * Calculates the Hex encoded HMAC-MD5 for the given key and data
 *
 * @param {string} k HMAC key
 * @param {string} d Input string
 * @returns {string} Raw MD5 string
 */
function hexHMACMD5(k, d) {
  return rstr2hex(rawHMACMD5(k, d))
}

/**
 * Calculates MD5 value for a given string.
 * If a key is provided, calculates the HMAC-MD5 value.
 * Returns a Hex encoded string unless the raw argument is given.
 *
 * @param {string} string Input string
 * @param {string} [key] HMAC key
 * @param {boolean} [raw] Raw output switch
 * @returns {string} MD5 output
 */
function md5(string) {
  return hexMD5(string)
}
//let out=Crypto.encrypt_aes_cbc(Crypto.pkcs_pad(new Uint8Array(ConvertUtf8.toBytes("aa")).buffer),iv.buffer,iv.buffer)
function encrypt(value,key){
  let k=md5(key)
  let kk=ConvertHex.toBytes(k);
  let kb=new UA8(kk);
  let out=Crypto.encrypt(Crypto.pkcs_pad(new UA8(ConvertUtf8.toBytes(value)).buffer),kb.buffer,kb.buffer)
  let hex_out=new UA8(out)
  return ConvertHex.fromBytes(hex_out)
}
function encryptb(value,key){
  let k=md5(key)
  let kk=ConvertHex.toBytes(k);
  let kb=new UA8(kk);
  let inputBytes=new UA8(ConvertUtf8.toBytes(value))
  let out=Crypto.encrypt(Crypto.pkcs_pad(inputBytes),kb.buffer,kb.buffer)
  let hex_out=new UA8(out)
  return btoa(String.fromCharCode.apply(null, hex_out))//ConvertHex.fromBytes(hex_out)
}
function decryptb(value,key){
  let k=md5(key)
  let kk=ConvertHex.toBytes(k);
  let kb=new UA8(kk);
  const data = new Uint8Array(atob(value).split('').map(char => char.charCodeAt(0)));
  //移除填充
  let out=Crypto.decrypt(Crypto.pkcs_unpad(new UA8(data).buffer),kb.buffer,kb.buffer)
  let hex_out=new UA8(out)
  return ConvertUtf8.fromBytes(hex_out)
}
export {Crypto,ConvertHex,ConvertUtf8,md5,encrypt,encryptb,decryptb}
